package com.polaris.live.common.util.loading

import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.os.SystemClock
import com.lxj.xpopup.XPopup
import com.lxj.xpopup.core.BasePopupView
import com.lxj.xpopup.interfaces.SimpleCallback
import com.polaris.live.common.R
import com.polaris.live.common.manager.withCountdown
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * LoadingPresenter
 *
 * @author Created by lucas on 2023/11/25 10:08
 */
class LoadingPresenter(private val mContext: Context) {

    /**
     * 是否使用view显示
     */
    private var mUseView: Boolean = false

    /**
     * 是否有阴影背景
     */
    private var mHasShadowBg: Boolean = false

    /**
     * 是否可以被取消
     */
    private var mIsCancelable: Boolean = false

    /**
     * 当超时取消是，是否显示时间
     */
    private var mIsShowTime: Boolean = false

    private var mLoadingDialog: LoadingDialog? = null
    private var mMaskDialog: Dialog? = null

    private var mIsShowed = false //是否调用了show
    private var mIsHided = true //是否取消了，只有调用hideLoading才不算被取消
    private var mIsTimeout = true //是否超时了，只有调用hideLoading才不算被取消

    private var mShowWaitJob: Job? = null
    private var mHideWaitJob: Job? = null
    private var mCancelJob: Job? = null

    private var mShowUptimeMs: Long = 0

    private var mOnCancelListener: OnCancelListener? = null
    private var mOnDismissListener: OnDismissListener? = null

    /**
     * 显示loading
     *
     * @param msg        内容
     * @param waitTime   等待显示时间
     * @param cancelTime 等待取消时间
     */
    fun showLoading(msg: String, waitTime: Long = 0, cancelTime: Long = 0) {
        if (mIsShowed) {
            return
        } else if (mContext is Activity && (mContext.isFinishing || mContext.isDestroyed)) {
            return
        }

        mIsShowed = true
        mIsHided = false
        mIsTimeout = false
        mShowUptimeMs = SystemClock.uptimeMillis()
        if (waitTime > 0) {
            mMaskDialog = Dialog(mContext, R.style.NotBackDialog).apply {
                setCancelable(mIsCancelable)
                setOnDismissListener {
                    mMaskDialog = null
                    dismissLoading()
                }
            }

            mMaskDialog?.show()

            mShowWaitJob = CoroutineScope(Dispatchers.Main).launch {
                try {
                    delay(waitTime)
                    if (mIsHided) {
                        return@launch
                    }
                    if (!isActivityAlive(mContext)) {
                        return@launch
                    }

                    mMaskDialog?.setOnDismissListener(null)
                    mMaskDialog?.dismiss()
                    mMaskDialog = null
                    openLoading(msg, cancelTime)
                } finally {
                    if (mShowWaitJob == coroutineContext[Job]) {
                        mShowWaitJob = null
                    }
                }
            }
        } else {
            openLoading(msg, cancelTime)
        }
    }

    private fun openLoading(msg: String, cancelTime: Long) {
        if (!isActivityAlive(mContext)) {
            return
        }

        mLoadingDialog = LoadingDialog(mContext, msg).apply {
            XPopup.Builder(mContext)
                .hasShadowBg(mHasShadowBg)
                .isDestroyOnDismiss(true)
                .setPopupCallback(object : SimpleCallback() {
                    override fun onDismiss(popupView: BasePopupView?) {
                        mLoadingDialog = null
                        dismissLoading()
                    }

                    override fun onBackPressed(popupView: BasePopupView?): Boolean {
                        if (mIsCancelable) {
                            popupView?.dismiss()
                        }
                        return mIsCancelable
                    }
                }).run {
                    if (mUseView) {
                        isViewMode(true)
                    } else {
                        this
                    }
                }
                .dismissOnTouchOutside(mIsCancelable)
                .dismissOnBackPressed(mIsCancelable)
                .asCustom(this)
                .show()
        }

        if (cancelTime > 0) {
            if (mIsShowTime) {
                mLoadingDialog?.setMessage("$msg(${cancelTime / 1000})")
            } else {
                mLoadingDialog?.setMessage(msg)
            }

            mCancelJob = CoroutineScope(Dispatchers.Main).launch {
                withCountdown(cancelTime, 1000, {
                    if (mIsShowTime && it > 0) {
                        mLoadingDialog?.setMessage("$msg(${it / 1000})")
                    }
                }) {
                    mIsTimeout = true
                    if (isActivityAlive(mContext)) {
                        mLoadingDialog?.dismiss()
                    }

                    if (mCancelJob == coroutineContext[Job]) {
                        mCancelJob = null
                    }
                }
            }
        } else {
            mLoadingDialog?.setMessage(msg)
        }

        mLoadingDialog?.show()
    }

    private fun dismissLoading() {
        mShowWaitJob?.cancel()
        mShowWaitJob = null

        mCancelJob?.cancel()
        mCancelJob = null

        mHideWaitJob?.cancel()
        mHideWaitJob = null

        mOnDismissListener?.onDismiss(this)

        mIsShowed = false

        if (!mIsHided) {
            mOnCancelListener?.onCancel(mIsTimeout)
        }
    }

    /**
     * 隐藏Loading
     *
     * @param waitTime 等待隐藏时间
     */
    fun hideLoading(waitTime: Long = 0) {
        if (!mIsShowed) {
            return
        }

        mHideWaitJob?.cancel()
        mHideWaitJob = null

        val delayTime = SystemClock.uptimeMillis() - mShowUptimeMs
        if (waitTime > delayTime) {
            mHideWaitJob = CoroutineScope(Dispatchers.Main).launch {
                try {
                    delay(waitTime - delayTime)

                    hideLoading()
                } finally {
                    if (mHideWaitJob == coroutineContext[Job]) {
                        mHideWaitJob = null
                    }
                }
            }
            return
        }

        mIsHided = true

        if (isActivityAlive(mContext)) {
            mMaskDialog?.dismiss()
            mLoadingDialog?.dismiss()
        } else {
            dismissLoading()
        }
    }

    /**
     * 设置是否使用view显示
     *
     * @param useView 是否
     */
    fun setUseView(useView: Boolean) {
        mUseView = useView
    }

    /**
     * 设置是否存在背景阴影
     *
     * @param hasShadowBg 是否
     */
    fun setShadowBg(hasShadowBg: Boolean) {
        mHasShadowBg = hasShadowBg
    }

    /**
     * 设置是否可以被取消
     *
     * @param isCancelable 是否
     */
    fun setCancelable(isCancelable: Boolean) {
        mIsCancelable = isCancelable
    }

    /**
     * 当超时取消是，是否显示时间
     *
     * @param isShowTime 是否
     */
    fun setShowTime(isShowTime: Boolean) {
        mIsShowTime = isShowTime
    }

    /**
     * 取消时回调
     *
     * @param l 回调
     */
    fun setOnCancelListener(l: OnCancelListener?) {
        mOnCancelListener = l
    }

    /**
     * 绑定Dismiss
     *
     * @param l 回调
     */
    fun setOnDismissListener(l: OnDismissListener?) {
        mOnDismissListener = l
    }

    /**
     * 解除回调
     */
    fun unbindListener() {
        mOnCancelListener = null
        mOnDismissListener = null
    }

    /**
     * 构建者模式，最终会自动解除回调
     */
    class Builder(private val mMessage: String) {

        private var mWaitTime: Long = 0
        private var mCancelTime: Long = 0

        private var mUseView = true
        private var mHasShadowBg = false
        private var mIsCancelable = false
        private var mIsShowTime = false

        private var mOnTimeoutCancelListener: OnCancelListener? = null
        private var mOnCancelListener: OnCancelListener? = null
        private var mOnDismissListener: OnDismissListener? = null

        /**
         * 设置等待显示时间
         *
         * @param waitTime 等待时间
         */
        fun setWaitTime(waitTime: Long): Builder {
            mWaitTime = waitTime
            return this
        }

        /**
         * 设置自动取消时间
         *
         * @param cancelTime 取消时间
         */
        fun setCancelTime(cancelTime: Long): Builder {
            mCancelTime = cancelTime
            return this
        }

        /**
         * 设置使用view显示
         *
         * @param useView 使用view显示
         */
        fun setUseView(useView: Boolean): Builder {
            mUseView = useView
            return this
        }

        /**
         * 设置是否存在阴影背景
         *
         * @param hasShadowBg 是否存在
         */
        fun setShadowBg(hasShadowBg: Boolean): Builder {
            mHasShadowBg = hasShadowBg
            return this
        }

        /**
         * 设置是否允许取消dialog
         *
         * @param isCancelable 是否允许
         */
        fun setCancelable(isCancelable: Boolean): Builder {
            mIsCancelable = isCancelable
            return this
        }

        /**
         * 当超时取消是，是否显示时间
         *
         * @param isShowTime 是否
         */
        fun setShowTime(isShowTime: Boolean): Builder {
            mIsShowTime = isShowTime
            return this
        }

        /**
         * 超时时回调
         *
         * @param l 回调
         */
        fun setOnTimeoutCancelListener(l: OnCancelListener): Builder {
            mOnTimeoutCancelListener = l
            return this
        }

        /**
         * 取消时回调
         *
         * @param l 回调
         */
        fun setOnCancelListener(l: OnCancelListener): Builder {
            mOnCancelListener = l
            return this
        }

        /**
         * 被销毁时回调
         *
         * @param l 回调
         */
        fun setOnDismissListener(l: OnDismissListener): Builder {
            mOnDismissListener = l
            return this
        }

        /**
         * 显示Loading
         *
         * @param loadingDialog loading帮助类
         */
        fun show(loadingDialog: LoadingPresenter) {
            loadingDialog.setUseView(mUseView)
            loadingDialog.setShadowBg(mHasShadowBg)
            loadingDialog.setCancelable(mIsCancelable)
            loadingDialog.setShowTime(mIsShowTime)
            loadingDialog.setOnCancelListener(mOnCancelListener)
            loadingDialog.setOnDismissListener {
                loadingDialog.unbindListener()

                mOnDismissListener?.onDismiss(loadingDialog)
            }

            loadingDialog.showLoading(mMessage, mWaitTime, mCancelTime)
        }

        /**
         * 构建一个loading帮助类同时显示
         *
         * @param context 上下文
         */
        fun showLoading(context: Context): LoadingPresenter {
            val loadingHelper = LoadingPresenter(context)

            show(loadingHelper)
            return loadingHelper
        }

    }

    companion object {

        /**
         * 创建一个loading构建类
         */
        fun create(message: String): Builder {
            return Builder(message)
        }

        /**
         * activity是否活跃
         */
        fun isActivityAlive(context: Context?): Boolean {
            if (context == null) {
                return false
            }
            if (context is Activity) {
                if (context.isFinishing) {
                    return false
                }
                return !context.isDestroyed
            }
            return true
        }
    }
}

fun interface OnCancelListener {

    /**
     * 取消
     *
     * @param timeout 是否超时取消
     */
    fun onCancel(timeout: Boolean)

}

fun interface OnDismissListener {

    /**
     * 销毁
     */
    fun onDismiss(loading: LoadingPresenter)

}